from owfs import Owfs, OWFSException
import sys, time, logging
from timers import Timers
from devio import Switches, Relays
from config import Config
from device import Sensor, Switch, Relay
import astro
from filter import DisturbanceFilter

logger = logging.getLogger(__name__)


class OWHandler:

    def __init__(self, configfilename):
        self.startup = True
        #polling interval [s]
        self.polInt = 0.1
        #auto discovery interval
        self.discInt = 5 / self.polInt    #5s
        self.discTimer = self.discInt
        #day/night check interval
        self.dnInt = 60 / self.polInt    #60s
        self.dnTimer = self.dnInt
        #detected devices
        self.devices = {}
        #timer collection for switch hold sensing and auto relay off
        self.timers = {Relays.Rly1 : Timers(), Relays.Rly2 : Timers(), Relays.Up : Timers(), Relays.Dn : Timers()}
        self.holdtimers = {Switches.Pir : Timers(), Switches.Sw1 : Timers(), Switches.Sw2 : Timers(), Switches.Sw3 : Timers(), Switches.Sw4 : Timers()}
        self.idletimers = {Switches.Pir : Timers(), Switches.Sw1 : Timers(), Switches.Sw2 : Timers(), Switches.Sw3 : Timers(), Switches.Sw4 : Timers()}    #TODO: zegary do filtrowania drgan stykow
        #sensed state memory
        self.sensed = {}
        #sustained monostable relays memory
        self.sustained = {}
        self.df = DisturbanceFilter(self.polInt)
        self.config = Config(configfilename)
        self.owfs = Owfs()
        logger.info("OWHandler initialized")

    def hubConfig(self):
        if self.startup:
            files = self.owfs.dir("")
            for file in files:
                if Owfs.isFamily(file, "EF"):
                    self.owfs.write(file + "/hub/branch.BYTE", "8")    #temporarily 12

    #TODO: zrezygnowac z domyslnej konfiguracji (wszystkie urzadzenia musza byc w ini)
    def devicesConfig(self):
        files = self.owfs.dir("")
        for file in files:
            if not file in self.devices:
                #TODO: write an alias for convinience
                if Owfs.isFamily(file, "29"):
                    self.devices[file] = 1
                    #switches DS2408
                    #say Hello!
                    if not self.startup:
                        for i in range(0, 4):
                            self.toggleRelay(file, Relays.Rly1)
                    if file not in self.config.devices:
                        self.config.devices[file] = Device(file)
                        logger.debug("Added default config:\n%s" % self.config.devices[file])
                    self.owfs.write(file + "/out_of_testmode", "1")
                    self.owfs.write(file + "/PIO.BYTE", "0")
                    self.owfs.write(file + "/por", "0")
                    self.owfs.write(file + "/set_alarm", "133333333")
                    #create timer
                    for relay in Relays.all:
                        if self.config.devices[file].type == Device._monostable:
                            self.timers[relay].addTimer(file)
                            logger.debug(self.timers[relay].timer)
                    #create state memory
                    if not file in self.sensed:
                        self.sensed[file] = 0
                    logger.debug(self.sensed)
                    #create filter
                    self.df.addDevice(file)
                elif Owfs.isFamily(file, "28"):
                    self.devices[file] = 1
                    #thermometers
                    #TODO: config.
                    #if file not in self.config.devices:
                        #self.config.devices[file] = Thermometer(file)
                        #logger.debug("Added default config:\n%s" % self.config.devices[file])
                    self.owfs.write(file + "/temphigh", "70")
                    self.owfs.write(file + "/templow", "0")
        logging.debug("Devices: %d" % len(self.devices))

        #delete data for no longer existing devices
        for name in self.devices.keys():
            if not name in files:
                del self.devices[name]
                self.df.removeDevice(name)
                if name in self.sensed:
                    del self.sensed[name]
        for relay in Relays.all:
            for name in self.timers[relay].timer.keys():
                if not name in files:
                    self.timers[relay].deleteTimer(name)
                    logger.debug(self.timers[relay].timer)


    
    def setRelay(self, file, rly):
        self.owfs.write(file + "/PIO." + str(rly), "1")
        logger.debug("Set relay: " + str(rly) + " on " + file)

    def clearRelay(self, file, rly):
        self.owfs.write(file + "/PIO." + str(rly), "0")

    def toggleRelay(self, file, rly):
        state = int(self.owfs.read(file + "/PIO." + str(rly)))
        if state:
            self.owfs.write(file + "/PIO." + str(rly), "0")
        else:
            self.owfs.write(file + "/PIO." + str(rly), "1")


    def handleDiscovery(self):
        if self.discTimer == self.discInt:
            self.discTimer = 0
            logger.debug("discovery")
            self.hubConfig()
            self.devicesConfig()
        self.discTimer += 1

    def handleAlarms(self):
        alarms = self.owfs.dir("alarm")
        for device in alarms:
            #switches
            alm = False
            try:
                if device in self.config.aliases and Switch.isFamily(self.config.aliases[device]):
                    self.df.alarm(device, int(self.owfs.read("/alarm/" + device + "/latch.BYTE")))
            except KeyError, e:
                logger.error("%s filter KeyError: no alias" % device)
                pass
            for switch in self.config.switches.values():
                if switch.alias == device:
                    alm = alm or switch.handleAlarms()
            #reset alarm
            if alm:
                Switch.resetAlarm(device)

    def handleTimers(self):
        for switch in self.config.switches.values():
            switch.handleTimers()
            if switch.redir:
                switch.redir.handleTimers()

    def setSustain(self, name, relay):
        if name not in self.sustained:
            self.sustained[name] = {}
        self.sustained[name][relay] = True
        logger.debug(self.sustained)

    def clearSustain(self, name, relay):
        if name in self.sustained and relay in self.sustained[name]:
            del self.sustained[name][relay]
            logger.debug(self.sustained)

    def isSustained(self, name, relay):
        logger.debug(self.sustained)
        if name in self.sustained and relay in self.sustained[name]:
            logger.debug("%s.%d sustained" % (name, relay))
            return True
        else:
            return False

    def handleDayNight(self):
        if self.dnTimer == self.dnInt:
            self.dnTimer = 0

            if astro.isDay():
                logger.debug("It is day")
                #TODO: invoke scene "scene day"

        self.dnTimer += 1


    def debugOutput(self, file, sw):
        logger.debug(file + " alarm: %s" % self.config.devices[file].addr)
        logger.debug(file + " alias: %s" % self.config.devices[file].alias)
        logger.debug(file + " output -> %s.%d" % (self.config.devices[file].redir[sw][0], self.config.devices[file].redir[sw][1]))

    def playScene(self, name):
        #TODO: implement
        logger.debug("Play scene " + name)



    def run (self):
        self.owfs.check()
        while True:
            #TODO: on start and periodicaly perform general interrogation
            #TODO: sw z timerami wylaczyc
            try:
                #TODO: reload config
                #TODO: test filter
                #self.df.checkAll()
                #self.handleDiscovery()
                #self.hubConfig()
                self.handleDayNight()
                self.handleAlarms()
                self.handleTimers()
                self.startup = False
            except OWFSException:
                self.owfs.recovery()
            except KeyError:
                pass
            time.sleep(self.polInt)
    



